
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>

#include "common_audio/vad/include/webrtc_vad.h"
#include "modules/audio_processing/aecm/echo_control_mobile.h"
#include "modules/audio_processing/agc/legacy/gain_control.h"
using namespace webrtc;

#if defined(WIN32) || defined(WIN64) || defined(WINCE) || defined(_MSC_VER)
/* ---------- Windows begin ---------- */
#ifdef __cplusplus
#define WEBRTC_ADAPT_EXPORT extern "C" __declspec(dllexport)
#else
#define WEBRTC_ADAPT_EXPORT __declspec(dllexport)
#endif
/* ---------- Windows end ---------- */
#elif defined(MACOS) || defined(MACOSX) || defined(APPLEOS)
/* ---------- mac & iOS begin ---------- */
#define WEBRTC_ADAPT_EXPORT
/* ---------- mac & iOS end ---------- */
#else
/* ---------- Linux begin ---------- */
#ifdef __cplusplus
#define WEBRTC_ADAPT_EXPORT extern "C"
#else
#define WEBRTC_ADAPT_EXPORT
#endif
/* ---------- Linux end ---------- */
#endif

typedef struct WebrtcAdaptHandle {
    void* handle;
    int32_t samplerate;
} WebrtcAdaptHandle;

/* ---------- vad ---------- */

WEBRTC_ADAPT_EXPORT void* webrtcadapt_vad_open(int32_t samplerate)
{
    VadInst* handle = NULL;
    int32_t ret = -1;
    do
    {
        handle = WebRtcVad_Create();
        if (!handle) {
            fprintf(stderr, "%s: WebRtcVad_Create failed \r\n", __FUNCTION__);
            break;
        }

        ret = WebRtcVad_Init(handle);
        if (ret != 0) {
            fprintf(stderr, "%s: WebRtcVad_Init failed(%d) \r\n", __FUNCTION__, ret);
            break;
        }

        /* 0~3 */
        ret = WebRtcVad_set_mode(handle, 1);
        if (ret != 0) {
            fprintf(stderr, "%s: WebRtcVad_set_mode failed(%d) mode 1 \r\n", __FUNCTION__, ret);
            break;
        }

    } while(0);

    if (ret != 0 && handle) {
        WebRtcVad_Free(handle);
    }

    if (ret == 0)
    {
        WebrtcAdaptHandle* waHandle = (WebrtcAdaptHandle*)malloc(sizeof(WebrtcAdaptHandle));
        waHandle->handle = (void*)handle;
        waHandle->samplerate = samplerate;
        return (void*)waHandle;
    }
    return NULL;
}

WEBRTC_ADAPT_EXPORT void webrtcadapt_vad_close(void* handle)
{
    WebrtcAdaptHandle* waHandle = (WebrtcAdaptHandle*)handle;
    WebRtcVad_Free((VadInst*)waHandle->handle);
    free(waHandle);
}

/*
 *  return:
 *      -1 : error
 *       0 : non-active voice
 *       1 : active voice
 */
WEBRTC_ADAPT_EXPORT int32_t webrtcadapt_vad_process(void* handle, uint8_t* frame, int32_t framesize)
{
    WebrtcAdaptHandle* waHandle = (WebrtcAdaptHandle*)handle;
    return WebRtcVad_Process((VadInst*)waHandle->handle, waHandle->samplerate, (int16_t*)frame, framesize/2);
}


/* ---------- agc ---------- */

WEBRTC_ADAPT_EXPORT void* webrtcadapt_agc_open(int32_t samplerate)
{
    void* handle = NULL;
    int32_t ret = -1;
    do
    {
        handle = WebRtcAgc_Create();
        if (!handle) {
            fprintf(stderr, "%s: WebRtcAgc_Create failed \r\n", __FUNCTION__);
            break;
        }

        ret = WebRtcAgc_Init(handle, 0, 100, kAgcModeAdaptiveDigital, samplerate);
        if (ret != 0) {
            fprintf(stderr, "%s: WebRtcAgc_Init failed(%d) samplerate %d \r\n", __FUNCTION__, ret, samplerate);
            break;
        }

        WebRtcAgcConfig config = {
            3, /* targetLevelDbfs: efault 3 (-3 dBOv) */
            9, /* compressionGaindB:  default 9 dB */
            kAgcTrue /* limiterEnable */
        };
        ret = WebRtcAgc_set_config(handle, config);
        if (ret != 0) {
            fprintf(stderr, "%s: WebRtcAgc_set_config failed(%d) \r\n", __FUNCTION__, ret);
            break;
        }
        
    } while(0);

    if (ret != 0 && handle) {
        WebRtcAgc_Free(handle);
    }

    if (ret == 0)
    {
        WebrtcAdaptHandle* waHandle = (WebrtcAdaptHandle*)malloc(sizeof(WebrtcAdaptHandle));
        waHandle->handle = handle;
        waHandle->samplerate = samplerate;
        return (void*)waHandle;
    }
    return NULL;
}

WEBRTC_ADAPT_EXPORT void webrtcadapt_agc_close(void* handle)
{
    WebrtcAdaptHandle* waHandle = (WebrtcAdaptHandle*)handle;
    WebRtcAgc_Free(waHandle->handle);
    free(waHandle);
}

/* default: 9 dB */
WEBRTC_ADAPT_EXPORT int32_t webrtcadapt_agc_getgain(void* handle)
{
    WebrtcAdaptHandle* waHandle = (WebrtcAdaptHandle*)handle;
    WebRtcAgcConfig config;
    int ret = WebRtcAgc_get_config(waHandle->handle, &config);
    return ret == 0 ? config.compressionGaindB : (-1);
}

WEBRTC_ADAPT_EXPORT void webrtcadapt_agc_setgain(void* handle, int32_t gain)
{
    WebrtcAdaptHandle* waHandle = (WebrtcAdaptHandle*)handle;
    WebRtcAgcConfig config;
    if (WebRtcAgc_get_config(waHandle->handle, &config) != 0) {
        return;
    }
    config.compressionGaindB = (int16_t)gain;
    WebRtcAgc_set_config(waHandle->handle, config);
}

/* return: 0: success */
WEBRTC_ADAPT_EXPORT int32_t webrtcadapt_agc_process(void* handle, uint8_t* frame, int32_t framesize)
{
    WebrtcAdaptHandle* waHandle = (WebrtcAdaptHandle*)handle;
    int32_t framesize10ms = waHandle->samplerate * 2 / 100;
    int16_t* in[1] = {NULL};
    int16_t* out[1] = {NULL};
    int32_t gains[11] = {0};
    int32_t ret = 0;
    while (framesize >= framesize10ms)
    {
        in[0] = (int16_t*)frame;
        out[0] = (int16_t*)frame;
        ret = WebRtcAgc_Process(waHandle->handle, gains, (const int16_t* const*)in, 1, (int16_t* const*)out);
        if (ret != 0) {
            return ret;
        }
        framesize -= framesize10ms;
        frame += framesize10ms;
    }
    return 0;
}


/* ---------- aec ---------- */

WEBRTC_ADAPT_EXPORT void* webrtcadapt_aec_open(int32_t samplerate)
{
    void* handle = NULL;
    int32_t ret = -1;
    do
    {
        handle = WebRtcAecm_Create();
        if (!handle) {
            fprintf(stderr, "%s: WebRtcAecm_Create failed \r\n", __FUNCTION__);
            break;
        }

        ret = WebRtcAecm_Init(handle, samplerate);
        if (ret != 0) {
            fprintf(stderr, "%s: WebRtcAecm_Init failed(%d) samplerate %d \r\n", __FUNCTION__, ret, samplerate);
            break;
        }

        AecmConfig config;
        config.cngMode = 0;
        config.echoMode = 1; /* 0~4 */
        ret = WebRtcAecm_set_config(handle, config);
        if (ret != 0) {
            fprintf(stderr, "%s: WebRtcAecm_set_config failed(%d) \r\n", __FUNCTION__, ret);
            break;
        }
        
    } while(0);

    if (ret != 0 && handle) {
        WebRtcAecm_Free(handle);
    }

    if (ret == 0)
    {
        WebrtcAdaptHandle* waHandle = (WebrtcAdaptHandle*)malloc(sizeof(WebrtcAdaptHandle));
        waHandle->handle = handle;
        waHandle->samplerate = samplerate;
        return (void*)waHandle;
    }
    return NULL;
}

WEBRTC_ADAPT_EXPORT void webrtcadapt_aec_close(void* handle)
{
    WebrtcAdaptHandle* waHandle = (WebrtcAdaptHandle*)handle;
    WebRtcAecm_Free(waHandle->handle);
    free(waHandle);
}

/* return: 0: success */
WEBRTC_ADAPT_EXPORT int32_t webrtcadapt_aec_process(
    void* handle, uint8_t* frame, int32_t framesize, int32_t delayms)
{
    WebrtcAdaptHandle* waHandle = (WebrtcAdaptHandle*)handle;
    return WebRtcAecm_Process(
        waHandle->handle,
        (const int16_t*)frame,
        NULL,
        (int16_t*)frame,
        framesize / 2,
        delayms);
}
